package demo8;

import java.lang.reflect.Constructor;

/**
 * 单例模式1: 饿汉式
 */
public class Main1 {
    public static void main(String[] args) throws Exception{
//        Singleton1 s1 = Singleton1.getInstance();
//        Singleton1 s2 = Singleton1.getInstance();
//        Singleton1 s3 = Singleton1.getInstance();
//        System.out.println(s1.hashCode());
//        System.out.println(s2.hashCode());
//        System.out.println(s3.hashCode());


        Constructor<Person> constructor = Person.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Person newSingleton = constructor.newInstance();

        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(()->{
                //Singleton2.getInstance();
                Person.getInstance();
            });
            t.start();
        }

    }
}

/*
* 饿汉式的优点
* 1、实现简单
* 2、在类加载的同时已经创建好一个静态对象，调用时反应速度快
* 3、在这个类每次加载到内存中，就把instance实例化好了
*   所以不存在在多线程中出现错误的情况，是线程安全的
* 饿汉式的缺点：
* 1、可能getInstance()方法永远不会执行到，但执行了该类的其他静态方法或加载了该类
* 但是这个实例仍然被初始化,因而浪费了资源
* 2、可能在还不需要此实例的时候就已经把实例创建出来了
*
* */
class Singleton1{

    // 1、私有化构造器
    private Singleton1(){}

    // 2、提供一个私有化的静态的当前类的对象
    private final static Singleton1 instance = new Singleton1();  // 饿汉式，一上来就创建了对象


    // 3、提供一个获取该实例的方法
    public static Singleton1 getInstance(){
        return instance;
    }

}

/*
* 懒汉式 : 会产生线程安全问题
* 解决办法一：同步方法，给整个方法加synchronized，锁粒度太大
* 解决办法二：双重检测锁 + volatile该类对象
*       知识点1：同步代码块synchronized (Singleton2.class){}
            obj 称为同步监视器，也就是锁
            原理是：当线程开始执行同步代码块前，必须先获得对同步代码块的锁定。
            并且任何时刻只能有一个线程可以获得对同步监视器的锁定
            当同步代码块执行完成后，该线程会释放对该同步监视器的锁定
            其中的锁，在非静态方法中可为this，
            在静态方法中为当前类本身（例如单例模式的懒汉式：singleInstance1.class）。
        知识点2：两次判断为空
                这样的话就不用每次都执行同步代码块，只需第一次执行的时候比较占资源
        知识点3：volatile该类对象：防止指令重排序
* */
class Singleton2{

    // 1、私有化构造器
    private Singleton2(){
        System.out.println("调用了懒汉式的构造器");
    };

    // 2、提供一个私有化的静态的当前类的变量用于接收该类对象,声明时未直接实例化
    private volatile static Singleton2 instance;

    // 3. 提供一个静态的获取该类对象的方法,但是对于多线程有问题
    public static Singleton2 getInstance(){
        // 双重检测锁，只允许获得一次锁,从而提高效率
        // 这样的话就不用每次都执行同步代码块，只需第一次执行的时候比较占资源，
        // 以后的话就和之前的一样了
        if(instance==null){
            synchronized (Singleton2.class){
                if(instance == null){
                    instance = new Singleton2();  // 懒汉式，等调用时才实例化对象
                }
            }
        }
        return instance;
    }

}

// 静态内部类
class Person{

    // 1
    private Person(){
        System.out.println("Person的构造函数");
    }

    // 2
    private static class Inner{
        private static final Person instance = new Person();
    }

    // 3
    public static final Person getInstance(){
        return Inner.instance;
    }

}



enum Test{

    INSTANCE;

    public void doSomething(){
        System.out.println("**");
    }

}